package xj.graph2d.table;

import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.swing.JCheckBox;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;

public class FeatureTable extends JTable {

  public FeatureTable(FeatureTableEntry prototype) {
    super(new ListTableModel(prototype));
    model = (ListTableModel) dataModel;
    initTable();
  }

  public FeatureTable(List<FeatureTableEntry> entries) {
    super(new ListTableModel(entries));
    model = (ListTableModel) dataModel;

    initTable();
  }

  protected void initTable() {
    getTableHeader().addMouseListener(new MouseAdapter() {
      @Override
      public void mousePressed(MouseEvent e) {
	Point p = e.getPoint();
	JTableHeader header = (JTableHeader) e.getSource();
	int column = header.columnAtPoint(p);
	if (model.sort(column)) {
	  clearSelection();
	  updateUI();
	}
      }
    });

    setRowSelectionAllowed(false);
    setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

    int columnCount = model.getColumnCount();
    for (int i = 0; i < columnCount; i++) {
      TableColumn column = getColumnModel().getColumn(i);
      TableCellRenderer renderer = model.getColumnRenderer(i);
      if (renderer != null) {
	column.setCellRenderer(renderer);
      }

      TableCellEditor editor = model.getColumnEditor(i);
      if (editor != null) {
	column.setCellEditor(editor);
      }

      int w = model.getColumnWidth(i);
      if (w > 0) {
	column.setPreferredWidth(w);
      }
    }
  }

  @Override
  public void editingStopped(ChangeEvent e) {
    super.editingStopped(e);

    // System.out.println("FeatureTable.editingStopped()");

    if (listener != null) {
      listener.editingStopped(e);
    }
  }

  public void addEntry(FeatureTableEntry e) {
    if (model != null) {
      model.addEntry(e);
      updateUI();
    }
  }

  public void setTableEntries(List<FeatureTableEntry> entries) {
    if (model != null) {
      model.setData(entries);
      updateUI();
    }
  }

  public void addListSelectionListener(ListSelectionListener x) {
    if (x != null) {
      ListSelectionModel lm = getSelectionModel();
      if (lm != null) {
	lm.addListSelectionListener(x);
      }
    }
  }

  public void addFeatureTableListener(FeatureTableListener listener) {
    this.listener = listener;
  }

  public void stopEditing() {
    if (isEditing()) {
      int columnCount = getColumnCount();
      int row = getSelectedRow();
      int col = getSelectedColumn();
      if (row >= 0 && col >= 0) {
	TableCellEditor editor = getCellEditor(row, col);
	if (editor != null) {
	  editor.stopCellEditing();
	}
      }
    }
  }

  @Override
  public String getToolTipText(MouseEvent event) {
    String tip = null;
    Point p = event.getPoint();

    // Locate the renderer under the event location
    int hitColumnIndex = columnAtPoint(p);
    if ((hitColumnIndex != -1)) {
      // String cname = model.getColumnName(i);
      tip = model.getColumnTip(hitColumnIndex);
    }

    // No tip from the renderer get our own tip
    if (tip == null) {
      tip = getToolTipText();
    }

    return tip;
  }

  protected ListTableModel model;

  protected FeatureTableListener listener;

  static class ListTableModel extends AbstractTableModel {

    public ListTableModel(List<FeatureTableEntry> entries) {
      if (entries != null && entries.size() > 0) {
	Object obj = entries.get(0);
	if (obj != null && obj instanceof FeatureTableEntry) {
	  this.prototype = (FeatureTableEntry) obj;
	  setData(entries);
	}
      }
    }

    public ListTableModel(FeatureTableEntry prototype) {
      this.prototype = prototype;
    }

    public int getColumnCount() {
      if (prototype != null) {
	return prototype.getColumnCount();
      }
      return 0;
    }

    public int getRowCount() {
      if (entries != null) {
	return entries.size();
      } else {
	return 0;
      }
    }

    @Override
    public String getColumnName(int col) {
      if (prototype != null) {
	return prototype.getColumnName(col);
      }
      return null;
    }

    public Object getValueAt(int row, int col) {
      if (entries != null) {
	FeatureTableEntry entry = getFeatureTableEntry(row);
	if (entry != null) {
	  return entry.getColumnValue(col);
	}
      }
      return null;
    }

    @Override
    public Class getColumnClass(int col) {
      if (prototype != null) {
	return prototype.getColumnClass(col);
      }
      return String.class;
    }

    public String getColumnTip(int col) {
      if (prototype != null) {
	return prototype.getColumnTip(col);
      }
      return null;
    }

    public Comparator getColumnComparator(int col) {
      if (prototype != null) {
	return prototype.getColumnComparator(col);
      }
      return null;
    }

    public TableCellEditor getColumnEditor(int col) {
      if (prototype != null) {
	return prototype.getColumnEditor(col);
      }
      return null;
    }

    public TableCellRenderer getColumnRenderer(int col) {
      if (prototype != null) {
	return prototype.getColumnRenderer(col);
      }
      return null;
    }

    public int getColumnWidth(int col) {
      if (prototype != null) {
	return prototype.getColumnWidth(col);
      }
      return -1;
    }

    @Override
    public boolean isCellEditable(int row, int col) {
      if (entries != null) {
	FeatureTableEntry entry = getFeatureTableEntry(row);
	if (entry != null) {
	  return entry.isColumnEditable(col);
	}
      }
      if (prototype != null) {
	return prototype.isColumnEditable(col);
      }
      return false;
    }

    @Override
    public void setValueAt(Object value, int row, int col) {
      if (entries != null) {
	FeatureTableEntry entry = getFeatureTableEntry(row);
	if (entry != null) {
	  entry.setColumnValue(col, value);
	}
      }
    }

    public void clearData() {
      entries = null;
    }

    public void setData(List<FeatureTableEntry> entries) {
      this.entries = entries;
    }

    public void addEntry(FeatureTableEntry e) {
      if (entries != null && e != null) {
	entries.add(e);
      }
    }

    public boolean sort(int col) {
      if (entries != null && col >= 0 && col < getColumnCount()) {
	Comparator c = getColumnComparator(col);
	if (c != null) {
	  Collections.sort(entries, c);
	  return true;
	}
      }
      return false;
    }

    public FeatureTableEntry getFeatureTableEntry(int i) {
      if (entries != null && i >= 0 && i < entries.size()) {
	return entries.get(i);
      }
      return null;
    }

    protected FeatureTableEntry prototype;

    protected List<FeatureTableEntry> entries;

  }

  static public class GrayBooleanRenderer extends JCheckBox implements
      TableCellRenderer {
    private static final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);

    public GrayBooleanRenderer() {
      super();
      setHorizontalAlignment(SwingConstants.CENTER);
      setBorderPainted(true);
    }

    public Component getTableCellRendererComponent(JTable table, Object value,
	boolean isSelected, boolean hasFocus, int row, int column) {
      if (isSelected) {
	setForeground(table.getSelectionForeground());
	super.setBackground(table.getSelectionBackground());
      } else {
	setForeground(table.getForeground());
	// setBackground(Color.lightGray);
	setBackground(Color.orange);
      }

      setSelected((value != null && ((Boolean) value).booleanValue()));
      TableModel tm = table.getModel();
      if (tm instanceof FeatureTable.ListTableModel) {
	FeatureTable.ListTableModel ltm = (FeatureTable.ListTableModel) tm;
	FeatureTableEntry entry = ltm.getFeatureTableEntry(row);
	if (entry != null && !entry.isEditable()) {
	  setEnabled(false);
	} else {
	  setEnabled(true);
	}
      }

      if (hasFocus) {
	setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
      } else {
	setBorder(noFocusBorder);
      }

      return this;
    }
  }

}
